import { defineEventHandler, getRouterParam, readBody } from "h3"; import { getCurrentUser, getConfigGlobal } from "#server/utils/context"; import { resolveCharacterIdentity } from "#server/service/character/identity"; import { getCharacterSessionByIdAndUser } from "#server/service/character/session"; import { getCharacterCardBySlug } from "#server/service/character/character-card"; import { executeCharacterChat } from "#server/service/character/chat-engine"; export default defineEventHandler(async (event) => { const slug = getRouterParam(event, "slug"); if (!slug) { throw createError({ statusCode: 400, statusMessage: "参数无效" }); } const card = await getCharacterCardBySlug(slug); if (!card) { throw createError({ statusCode: 404, statusMessage: "角色不存在" }); } const identity = await resolveCharacterIdentity(event); if (!identity.userId && !identity.tempToken) { throw createError({ statusCode: 401, statusMessage: "请先登录或创建会话" }); } const body = await readBody(event); const { sessionId, content, modelId } = body as { sessionId: string; content?: string; modelId?: number | null; }; if (!sessionId) { throw createError({ statusCode: 400, statusMessage: "参数无效" }); } if (typeof content !== "string" || content.trim().length === 0) { throw createError({ statusCode: 400, statusMessage: "内容不能为空" }); } const session = await getCharacterSessionByIdAndUser(sessionId, identity.userId, identity.tempToken); if (!session) { throw createError({ statusCode: 404, statusMessage: "会话不存在" }); } if (session.cardId !== card.id) { throw createError({ statusCode: 400, statusMessage: "会话不属于该角色" }); } const preferredModelId = modelId ?? null; const globalDefaultModelId = (await getConfigGlobal("agentDefaultModelId")) ?? null; const user = identity.userId ? await getCurrentUser(event) : null; const { response } = await executeCharacterChat({ card, session, user: user ? { id: user.id } : null, content, preferredModelId, globalDefaultModelId, requestId: event.context.requestId, onClientClose: (cb) => { event.node.req.on("close", cb); }, }); return response; });